#ifndef BEHAVED_H
#define BEHAVED_H
#include "common.h"
#include "buff.h"

class Behaved {
public:

    // Don't bother running anything if it's already destroyed
    virtual       void Destroy() = 0;
    virtual       bool IsDestroyed() const = 0;
    virtual       Map* GetMap() = 0;
    virtual const Map* GetMap() const = 0;

    // Active buffs
    friend class Buff;
    virtual bool HasBuff( const Buff* buff ) const;
    virtual void GetAllBuffs     ( BuffList& outputList );
    virtual void GetAllBuffs( ConstBuffList& outputList ) const;

    // Base implementer
    virtual const Behaved* GetBase() const;
    virtual       Behaved* GetBase();

    // Call the desired behaviour on this actor. Returns true if
    // any buff on this actor, or the actor itself implemented
    // the desired behaviour -- i.e., if anything was done.
    template< class BehaviourInterface >
    bool Call( typename BehaviourInterface::ArgType0 a0 = 0, typename BehaviourInterface::ArgType1 a1 = 0,
               typename BehaviourInterface::ArgType2 a2 = 0, typename BehaviourInterface::ArgType3 a3 = 0,
               typename BehaviourInterface::ArgType4 a4 = 0, typename BehaviourInterface::ArgType5 a5 = 0,
               typename BehaviourInterface::ArgType6 a6 = 0, typename BehaviourInterface::ArgType7 a7 = 0 );
    template< class BehaviourInterface >
    bool Call( typename BehaviourInterface::ArgType0 a0 = 0, typename BehaviourInterface::ArgType1 a1 = 0,
               typename BehaviourInterface::ArgType2 a2 = 0, typename BehaviourInterface::ArgType3 a3 = 0,
               typename BehaviourInterface::ArgType4 a4 = 0, typename BehaviourInterface::ArgType5 a5 = 0,
               typename BehaviourInterface::ArgType6 a6 = 0, typename BehaviourInterface::ArgType7 a7 = 0 ) const;
    template< class BehaviourInterface >
    bool Has( bool countBuffs = true ) const;

    // Useful helpful functions
    MessageBank& Bank();
    bool IsPlayer() const;
    void QueueMessage( const     ustring& message, const IO::SymList& firstChar = IO::SymList() );
    void QueueMessage( const std::string& message, const IO::SymList& firstChar = IO::SymList() );
    void QueueMessage( const     ustring& message, uint before );
    void QueueMessage( const std::string& message, uint before );
    void DisplayMessages();

private:

    friend class Item;
    virtual void Add( Buff* buff );
    virtual void Remove( Buff* buff );

    // Active buffs
    BuffSet _buffs;

};

// Behaviour call implementation
template< class BehaviourInterface >
bool Behaved::Call( typename BehaviourInterface::ArgType0 a0, typename BehaviourInterface::ArgType1 a1,
                  typename BehaviourInterface::ArgType2 a2, typename BehaviourInterface::ArgType3 a3,
                  typename BehaviourInterface::ArgType4 a4, typename BehaviourInterface::ArgType5 a5,
                  typename BehaviourInterface::ArgType6 a6, typename BehaviourInterface::ArgType7 a7 )
{
    if ( IsDestroyed() )
        return false;
    bool implemented = false;

    BehaviourInterface::Start( a0, a1, a2, a3, a4, a5, a6, a7 );

    BuffList list;
    GetAllBuffs( list );
    foreach ( Buff* b, list ) {
        b->SetApplyingPostActor( false );
        BehaviourInterface* bi = dynamic_cast< BehaviourInterface* >( b );
        if ( bi && !b->IsDestroyed() ) {
            implemented = true;
            if ( !bi->ICall( a0, a1, a2, a3, a4, a5, a6, a7 ) ) {
                BehaviourInterface::End( a0, a1, a2, a3, a4, a5, a6, a7 );
                return true;
            }
        }

        if ( IsDestroyed() ) {
            BehaviourInterface::End( a0, a1, a2, a3, a4, a5, a6, a7 );
            return implemented;
        }
    }

    BehaviourInterface* bi = dynamic_cast< BehaviourInterface* >( GetBase() );
    if ( bi ) {
        implemented = true;
        if ( !bi->ICall( a0, a1, a2, a3, a4, a5, a6, a7 ) ) {
            BehaviourInterface::End( a0, a1, a2, a3, a4, a5, a6, a7 );
            return true;
        }
    }

    if ( IsDestroyed() ) {
        BehaviourInterface::End( a0, a1, a2, a3, a4, a5, a6, a7 );
        return implemented;
    }

    foreach ( Buff* b, list ) {
        b->SetApplyingPostActor( true );
        BehaviourInterface* bi = dynamic_cast< BehaviourInterface* >( b );
        if ( bi && !b->IsDestroyed() ) {
            implemented = true;
            if ( !bi->ICall( a0, a1, a2, a3, a4, a5, a6, a7 ) ) {
                BehaviourInterface::End( a0, a1, a2, a3, a4, a5, a6, a7 );
                return true;
            }
        }

        if ( IsDestroyed() ) {
            BehaviourInterface::End( a0, a1, a2, a3, a4, a5, a6, a7 );
            return implemented;
        }
    }

    BehaviourInterface::End( a0, a1, a2, a3, a4, a5, a6, a7 );
    return implemented;
}

template< class BehaviourInterface >
bool Behaved::Call( typename BehaviourInterface::ArgType0 a0, typename BehaviourInterface::ArgType1 a1,
                  typename BehaviourInterface::ArgType2 a2, typename BehaviourInterface::ArgType3 a3,
                  typename BehaviourInterface::ArgType4 a4, typename BehaviourInterface::ArgType5 a5,
                  typename BehaviourInterface::ArgType6 a6, typename BehaviourInterface::ArgType7 a7 ) const
{
    if ( IsDestroyed() )
        return false;
    bool implemented = false;

    BehaviourInterface::Start( a0, a1, a2, a3, a4, a5, a6, a7 );

    ConstBuffList list;
    GetAllBuffs( list );
    foreach( const Buff* b, list ) {
        b->SetApplyingPostActor( false );
        const BehaviourInterface* bi = dynamic_cast< const BehaviourInterface* >( b );
        if ( bi && !b->IsDestroyed() ) {
            implemented = true;
            if ( !bi->ICall( a0, a1, a2, a3, a4, a5, a6, a7 ) ) {
                BehaviourInterface::End( a0, a1, a2, a3, a4, a5, a6, a7 );
                return true;
            }
        }

        if ( IsDestroyed() ) {
            BehaviourInterface::End( a0, a1, a2, a3, a4, a5, a6, a7 );
            return implemented;
        }
    }

    const BehaviourInterface* bi = dynamic_cast< const BehaviourInterface* >( GetBase() );
    if ( bi ) {
        implemented = true;
        if ( !bi->ICall( a0, a1, a2, a3, a4, a5, a6, a7 ) ) {
            BehaviourInterface::End( a0, a1, a2, a3, a4, a5, a6, a7 );
            return true;
        }
    }

    if ( IsDestroyed() ) {
        BehaviourInterface::End( a0, a1, a2, a3, a4, a5, a6, a7 );
        return implemented;
    }

    foreach( const Buff* b, list ) {
        b->SetApplyingPostActor( true );
        const BehaviourInterface* bi = dynamic_cast< const BehaviourInterface* >( b );
        if ( bi && !b->IsDestroyed() ) {
            implemented = true;
            if ( !bi->ICall( a0, a1, a2, a3, a4, a5, a6, a7 ) ) {
                BehaviourInterface::End( a0, a1, a2, a3, a4, a5, a6, a7 );
                return true;
            }
        }

        if ( IsDestroyed() ) {
            BehaviourInterface::End( a0, a1, a2, a3, a4, a5, a6, a7 );
            return implemented;
        }
    }

    return implemented;
}

template< class BehaviourInterface >
bool Behaved::Has( bool countBuffs ) const
{
    if ( dynamic_cast< const BehaviourInterface* >( GetBase() ) )
        return true;
    if ( countBuffs ) {
        ConstBuffList list;
        GetAllBuffs( list );
        foreach( const Buff* b, list ) {
            if ( dynamic_cast< const BehaviourInterface* >( b ) )
                return true;
        }
    }
    return false;
}

#endif